//
//  CG.swift
//  CooperSwift
//
//  Created by 计海峰 on 2021/6/1.
//

import Foundation

// MARK: CGPoint

public extension CGPoint {

    /// Creates a point with unnamed arguments.
    init(_ x: CGFloat, _ y: CGFloat) {
        self.init()
        self.x = x
        self.y = y
    }

    /// Returns a copy with the x value changed.
    func with(x: CGFloat) -> CGPoint {
        return CGPoint(x: x, y: y)
    }
    /// Returns a copy with the y value changed.
    func with(y: CGFloat) -> CGPoint {
        return CGPoint(x: x, y: y)
    }
}

// MARK: CGSize

public extension CGSize {

    /// Creates a size with unnamed arguments.
    init(_ width: CGFloat, _ height: CGFloat) {
        self.init()
        self.width = width
        self.height = height
    }

    /// Returns a copy with the width value changed.
    func with(width: CGFloat) -> CGSize {
        return CGSize(width: width, height: height)
    }
    /// Returns a copy with the height value changed.
    func with(height: CGFloat) -> CGSize {
        return CGSize(width: width, height: height)
    }
}

// MARK: CGRect

public extension CGRect {

    /// Creates a rect with unnamed arguments.
    init(_ origin: CGPoint, _ size: CGSize) {
        self.init()
        self.origin = origin
        self.size = size
    }

    /// Creates a rect with unnamed arguments.
    init(_ x: CGFloat, _ y: CGFloat, _ width: CGFloat, _ height: CGFloat) {
        self.init()
        self.origin = CGPoint(x: x, y: y)
        self.size = CGSize(width: width, height: height)
    }

    // MARK: access shortcuts

    /// Alias for origin.x.
    var x: CGFloat {
        get {return origin.x}
        set {origin.x = newValue}
    }
    /// Alias for origin.y.
    var y: CGFloat {
        get {return origin.y}
        set {origin.y = newValue}
    }

    /// Accesses origin.x + 0.5 * size.width.
    var centerX: CGFloat {
        get {return x + width * 0.5}
        set {x = newValue - width * 0.5}
    }
    /// Accesses origin.y + 0.5 * size.height.
    var centerY: CGFloat {
        get {return y + height * 0.5}
        set {y = newValue - height * 0.5}
    }

    // MARK: edges

    /// Alias for origin.x.
    var left: CGFloat {
        get {return origin.x}
        set {origin.x = newValue}
    }
    /// Accesses origin.x + size.width.
    var right: CGFloat {
        get {return x + width}
        set {x = newValue - width}
    }

    #if os(iOS)
    /// Alias for origin.y.
    var top: CGFloat {
        get {return y}
        set {y = newValue}
    }
    /// Accesses origin.y + size.height.
    var bottom: CGFloat {
        get {return y + height}
        set {y = newValue - height}
    }
    #else
    /// Accesses origin.y + size.height.
    var top: CGFloat {
        get {return y + height}
        set {y = newValue - height}
    }
    /// Alias for origin.y.
    var bottom: CGFloat {
        get {return y}
        set {y = newValue}
    }
    #endif

    // MARK: points

    /// Accesses the point at the top left corner.
    var topLeft: CGPoint {
        get {return CGPoint(x: left, y: top)}
        set {left = newValue.x; top = newValue.y}
    }
    /// Accesses the point at the middle of the top edge.
    var topCenter: CGPoint {
        get {return CGPoint(x: centerX, y: top)}
        set {centerX = newValue.x; top = newValue.y}
    }
    /// Accesses the point at the top right corner.
    var topRight: CGPoint {
        get {return CGPoint(x: right, y: top)}
        set {right = newValue.x; top = newValue.y}
    }

    /// Accesses the point at the middle of the left edge.
    var centerLeft: CGPoint {
        get {return CGPoint(x: left, y: centerY)}
        set {left = newValue.x; centerY = newValue.y}
    }
    /// Accesses the point at the center.
    var center: CGPoint {
        get {return CGPoint(x: centerX, y: centerY)}
        set {centerX = newValue.x; centerY = newValue.y}
    }
    /// Accesses the point at the middle of the right edge.
    var centerRight: CGPoint {
        get {return CGPoint(x: right, y: centerY)}
        set {right = newValue.x; centerY = newValue.y}
    }

    /// Accesses the point at the bottom left corner.
    var bottomLeft: CGPoint {
        get {return CGPoint(x: left, y: bottom)}
        set {left = newValue.x; bottom = newValue.y}
    }
    /// Accesses the point at the middle of the bottom edge.
    var bottomCenter: CGPoint {
        get {return CGPoint(x: centerX, y: bottom)}
        set {centerX = newValue.x; bottom = newValue.y}
    }
    /// Accesses the point at the bottom right corner.
    var bottomRight: CGPoint {
        get {return CGPoint(x: right, y: bottom)}
        set {right = newValue.x; bottom = newValue.y}
    }

    // MARK: with

    /// Returns a copy with the origin value changed.
    func with(origin: CGPoint) -> CGRect {
        return CGRect(origin: origin, size: size)
    }
    /// Returns a copy with the x and y values changed.
    func with(x: CGFloat, y: CGFloat) -> CGRect {
        return with(origin: CGPoint(x: x, y: y))
    }
    /// Returns a copy with the x value changed.
    func with(x: CGFloat) -> CGRect {
        return with(x: x, y: y)
    }
    /// Returns a copy with the y value changed.
    func with(y: CGFloat) -> CGRect {
        return with(x: x, y: y)
    }

    /// Returns a copy with the size value changed.
    func with(size: CGSize) -> CGRect {
        return CGRect(origin: origin, size: size)
    }
    /// Returns a copy with the width and height values changed.
    func with(width: CGFloat, height: CGFloat) -> CGRect {
        return with(size: CGSize(width: width, height: height))
    }
    /// Returns a copy with the width value changed.
    func with(width: CGFloat) -> CGRect {
        return with(width: width, height: height)
    }
    /// Returns a copy with the height value changed.
    func with(height: CGFloat) -> CGRect {
        return with(width: width, height: height)
    }

    /// Returns a copy with the x and width values changed.
    func with(x: CGFloat, width: CGFloat) -> CGRect {
        return CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height))
    }
    /// Returns a copy with the y and height values changed.
    func with(y: CGFloat, height: CGFloat) -> CGRect {
        return CGRect(origin: CGPoint(x: x, y: y), size: CGSize(width: width, height: height))
    }

    // MARK: offset

    /// Returns a copy with the x and y values offset.
    func offsetBy(dx: CGFloat, _ dy: CGFloat) -> CGRect {
        return with(x: x + dx, y: y + dy)
    }
    /// Returns a copy with the x value values offset.
    func offsetBy(dx: CGFloat) -> CGRect {
        return with(x: x + dx)
    }
    /// Returns a copy with the y value values offset.
    func offsetBy(dy: CGFloat) -> CGRect {
        return with(y: y + dy)
    }
    /// Returns a copy with the x and y values offset.
    func offsetBy(by: CGSize) -> CGRect {
        return with(x: x + by.width, y: y + by.height)
    }

    /// Modifies the x and y values by offsetting.
//    mutating func offsetInPlace(dx: CGFloat, _ dy: CGFloat) {
//        offsetInPlace(dx: dx, dy)
//    }
    /// Modifies the x value values by offsetting.
    mutating func offsetInPlace(dx: CGFloat = 0) {
        x += dx
    }
    /// Modifies the y value values by offsetting.
    mutating func offsetInPlace(dy: CGFloat = 0) {
        y += dy
    }
    /// Modifies the x and y values by offsetting.
//    mutating func offsetInPlace(by: CGSize) {
//        offsetInPlace(dx: by.width, by.height)
//    }

    // MARK: inset

    /// Returns a copy inset on all edges by the same value.
    func insetBy(by: CGFloat) -> CGRect {
        return insetBy(dx: by, dy: by)
    }

    /// Returns a copy inset on the left and right edges.
    func insetBy(dx: CGFloat) -> CGRect {
        return with(x: x + dx, width: width - dx * 2)
    }
    /// Returns a copy inset on the top and bottom edges.
    func insetBy(dy: CGFloat) -> CGRect {
        return with(y: y + dy, height: height - dy * 2)
    }

    /// Returns a copy inset on all edges by different values.
    func insetBy(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) -> CGRect {
        return CGRect(x: x + minX, y: y + minY, width: width - minX - maxX, height: height - minY - maxY)
    }

    /// Returns a copy inset on all edges by different values.
    func insetBy(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> CGRect {
        #if os(iOS)
            return CGRect(x: x + left, y: y + top, width: width - right - left, height: height - top - bottom)
        #else
            return CGRect(x: x + left, y: y + bottom, width: width - right - left, height: height - top - bottom)
        #endif
    }

    /// Returns a copy inset on the top and left edges.
    func insetBy(topLeft: CGSize) -> CGRect {
        return insetBy(top: topLeft.height, left: topLeft.width)
    }
    /// Returns a copy inset on the top and right edges.
    func insetBy(topRight: CGSize) -> CGRect {
        return insetBy(top: topRight.height, right: topRight.width)
    }
    /// Returns a copy inset on the bottom and left edges.
    func insetBy(bottomLeft: CGSize) -> CGRect {
        return insetBy(left: bottomLeft.width, bottom: bottomLeft.height)
    }
    /// Returns a copy inset on the bottom and right edges.
    func insetBy(bottomRight: CGSize) -> CGRect {
        return insetBy(bottom: bottomRight.height, right: bottomRight.width)
    }

//    /// Modifies all values by insetting all edges by the same value.
//    mutating func insetInPlace(by: CGFloat) {
//        insetInPlace(dx: by, dy: by)
//    }
//
//    /// Modifies all values by insetting on the left and right edges.
//    mutating func insetInPlace(dx: CGFloat) {
//        insetInPlace(dx: dx, dy: 0)
//    }
//    /// Modifies all values by insetting on the top and bottom edges.
//    mutating func insetInPlace(dy: CGFloat) {
//        insetInPlace(dx: 0, dy: dy)
//    }

    /// Modifies all values by insetting all edges by different value.
    mutating func insetInPlace(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) {
        self = insetBy(minX: minX, minY: minY, maxX: maxX, maxY: maxY)
    }

    /// Modifies all values by insetting all edges by different value.
    mutating func insetInPlace(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
        self = insetBy(top: top, left: left, bottom: bottom, right: right)
    }

    /// Modifies all values by insetting the top and left edges.
    mutating func insetInPlace(topLeft: CGSize) {
        self = insetBy(topLeft: topLeft)
    }
    /// Modifies all values by insetting the top and right edges.
    mutating func insetInPlace(topRight: CGSize) {
        self = insetBy(topRight: topRight)
    }
    /// Modifies all values by insetting the bottom and left edges.
    mutating func insetInPlace(bottomLeft: CGSize) {
        self = insetBy(bottomLeft: bottomLeft)
    }
    /// Modifies all values by insetting the bottom and right edges.
    mutating func insetInPlace(bottomRight: CGSize) {
        self = insetBy(bottomRight: bottomRight)
    }

    // MARK: extending

    /// Returns a copy extended on all edges by different values.
    func extendBy(dx: CGFloat, dy: CGFloat = 0) -> CGRect {
        return insetBy(dx: -dx, dy: -dy)
    }
    /// Returns a copy extended on the top and bottom edges.
    func extendBy(dy: CGFloat) -> CGRect {
        return insetBy(dy: -dy)
    }

    /// Returns a copy extended on all edges by the same value.
    func extendBy(by: CGFloat) -> CGRect {
        return insetBy(dx: -by, dy: -by)
    }

    /// Returns a copy extended on all edges by different values.
    func extendBy(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) -> CGRect {
        return insetBy(minX: -minX, minY: -minY, maxX: -maxX, maxY: -maxY)
    }
    /// Returns a copy extended on all edges by different values.
    func extendBy(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) -> CGRect {
        return insetBy(top: -top, left: -left, bottom: -bottom, right: -right)
    }

    /// Modifies all values by extending the top and left edges.
    func extendBy(topLeft: CGSize) -> CGRect {
        return extendBy(top: topLeft.height, left: topLeft.width)
    }
    /// Modifies all values by extending the top and right edges.
    func extendBy(topRight: CGSize) -> CGRect {
        return insetBy(top: -topRight.height, right: -topRight.width)
    }
    /// Modifies all values by extending the bottom and left edges.
    func extendBy(bottomLeft: CGSize) -> CGRect {
        return insetBy(left: -bottomLeft.width, bottom: -bottomLeft.height)
    }
    /// Modifies all values by extending the bottom and right edges.
    func extendBy(bottomRight: CGSize) -> CGRect {
        return insetBy(bottom: -bottomRight.height, right: -bottomRight.width)
    }

    /// Modifies all values by extending all edges by different values.
    mutating func extendInPlace(dx: CGFloat, dy: CGFloat = 0) {
        self = insetBy(dx: -dx, dy: -dy)
    }
    /// Modifies all values by extending the top and bottom edges.
    mutating func extendInPlace(dy: CGFloat) {
        self = insetBy(dy: -dy)
    }

    /// Modifies all values by extending all edges by the same value.
    mutating func extendInPlace(by: CGFloat) {
        self = insetBy(dx: -by, dy: -by)
    }

    /// Modifies all values by extending all edges by different values.
    mutating func extendInPlace(minX: CGFloat = 0, minY: CGFloat = 0, maxX: CGFloat = 0, maxY: CGFloat = 0) {
        self = insetBy(minX: -minX, minY: -minY, maxX: -maxX, maxY: -maxY)
    }
    /// Modifies all values by extending all edges by different values.
    mutating func extendInPlace(top: CGFloat = 0, left: CGFloat = 0, bottom: CGFloat = 0, right: CGFloat = 0) {
        self = insetBy(top: -top, left: -left, bottom: -bottom, right: -right)
    }

    /// Modifies all values by extending the top and left edges.
    mutating func extendInPlace(topLeft: CGSize) {
        self = extendBy(top: topLeft.height, left: topLeft.width)
    }
    /// Modifies all values by extending the top and right edges.
    mutating func extendInPlace(topRight: CGSize) {
        self = insetBy(top: -topRight.height, right: -topRight.width)
    }
    /// Modifies all values by extending the bottom and left edges.
    mutating func extendInPlace(bottomLeft: CGSize) {
        self = insetBy(left: -bottomLeft.width, bottom: -bottomLeft.height)
    }
    /// Modifies all values by extending the bottom and right edges.
    mutating func extendInPlace(bottomRight: CGSize) {
        self = insetBy(bottom: -bottomRight.height, right: -bottomRight.width)
    }

    // MARK: sizes

    /// Returns a rect of the specified size centered in this rect.
    func center(size: CGSize) -> CGRect {
        let dx = width - size.width
        let dy = height - size.height
        return CGRect(x: x + dx * 0.5, y: y + dy * 0.5, width: size.width, height: size.height)
    }

    /// Returns a rect of the specified size centered in this rect touching the specified edge.
    func center(size: CGSize, alignTo edge: CGRectEdge) -> CGRect {
        return CGRect(origin: alignedOrigin(size: size, edge: edge), size: size)
    }

    private func alignedOrigin(size: CGSize, edge: CGRectEdge) -> CGPoint {
        let dx = width - size.width
        let dy = height - size.height
        switch edge {
        case .minXEdge:
            return CGPoint(x: x, y: y + dy * 0.5)
        case .minYEdge:
            return CGPoint(x: x + dx * 0.5, y: y)
        case .maxXEdge:
            return CGPoint(x: x + dx, y: y + dy * 0.5)
        case .maxYEdge:
            return CGPoint(x: x + dx * 0.5, y: y + dy)
        }
    }

    /// Returns a rect of the specified size centered in this rect touching the specified corner.
    func align(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) -> CGRect {
        return CGRect(origin: alignedOrigin(size: size, corner: e1, e2), size: size)
    }

    private func alignedOrigin(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) -> CGPoint {
        let dx = width - size.width
        let dy = height - size.height
        switch (e1, e2) {
        case (.minXEdge, .minYEdge), (.minYEdge, .minXEdge):
            return CGPoint(x: x, y: y)
        case (.maxXEdge, .minYEdge), (.minYEdge, .maxXEdge):
            return CGPoint(x: x + dx, y: y)
        case (.minXEdge, .maxYEdge), (.maxYEdge, .minXEdge):
            return CGPoint(x: x, y: y + dy)
        case (.maxXEdge, .maxYEdge), (.maxYEdge, .maxXEdge):
            return CGPoint(x: x + dx, y: y + dy)
        default:
            preconditionFailure("Cannot align to this combination of edges")
        }
    }

    /// Modifies all values by setting the size while centering the rect.
    mutating func centerInPlace(size: CGSize) {
        self = center(size: size)
    }

    /// Modifies all values by setting the size while centering the rect touching the specified edge.
    mutating func centerInPlace(size: CGSize, alignTo edge: CGRectEdge) {
        self = center(size: size, alignTo: edge)
    }

    /// Modifies all values by setting the size while centering the rect touching the specified corner.
    mutating func alignInPlace(size: CGSize, corner e1: CGRectEdge, _ e2: CGRectEdge) {
        self = align(size: size, corner: e1, e2)
    }
}

// MARK: transform

extension CGAffineTransform: CustomDebugStringConvertible {
    public var debugDescription: String {
        return "(\(a),\(b),\(c),\(d),\(tx),\(ty))"
    }
}

// MARK: operators

/// Returns a point by adding the coordinates of another point.
public func +(p1: CGPoint, p2: CGPoint) -> CGPoint {
    return CGPoint(x: p1.x + p2.x, y: p1.y + p2.y)
}
/// Modifies the x and y values by adding the coordinates of another point.
public func +=( p1: inout CGPoint, p2: CGPoint) {
    p1.x += p2.x
    p1.y += p2.y
}
/// Returns a point by subtracting the coordinates of another point.
public func -(p1: CGPoint, p2: CGPoint) -> CGPoint {
    return CGPoint(x: p1.x - p2.x, y: p1.y - p2.y)
}
/// Modifies the x and y values by subtracting the coordinates of another points.
public func -=( p1: inout CGPoint, p2: CGPoint) {
    p1.x -= p2.x
    p1.y -= p2.y
}

/// Returns a point by adding a size to the coordinates.
public func +(point: CGPoint, size: CGSize) -> CGPoint {
    return CGPoint(x: point.x + size.width, y: point.y + size.height)
}
/// Modifies the x and y values by adding a size to the coordinates.
public func +=( point: inout CGPoint, size: CGSize) {
    point.x += size.width
    point.y += size.height
}
/// Returns a point by subtracting a size from the coordinates.
public func -(point: CGPoint, size: CGSize) -> CGPoint {
    return CGPoint(x: point.x - size.width, y: point.y - size.height)
}
/// Modifies the x and y values by subtracting a size from the coordinates.
public func -=( point: inout CGPoint, size: CGSize) {
    point.x -= size.width
    point.y -= size.height
}

/// Returns a point by adding a tuple to the coordinates.
public func +(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint {
    return CGPoint(x: point.x + tuple.0, y: point.y + tuple.1)
}
/// Modifies the x and y values by adding a tuple to the coordinates.
public func +=( point: inout CGPoint, tuple: (CGFloat, CGFloat)) {
    point.x += tuple.0
    point.y += tuple.1
}
/// Returns a point by subtracting a tuple from the coordinates.
public func -(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint {
    return CGPoint(x: point.x - tuple.0, y: point.y - tuple.1)
}
/// Modifies the x and y values by subtracting a tuple from the coordinates.
public func -=( point: inout CGPoint, tuple: (CGFloat, CGFloat)) {
    point.x -= tuple.0
    point.y -= tuple.1
}
/// Returns a point by multiplying the coordinates with a value.
public func *(point: CGPoint, factor: CGFloat) -> CGPoint {
    return CGPoint(x: point.x * factor, y: point.y * factor)
}
/// Modifies the x and y values by multiplying the coordinates with a value.
public func *=( point: inout CGPoint, factor: CGFloat) {
    point.x *= factor
    point.y *= factor
}
/// Returns a point by multiplying the coordinates with a tuple.
public func *(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint {
    return CGPoint(x: point.x * tuple.0, y: point.y * tuple.1)
}
/// Modifies the x and y values by multiplying the coordinates with a tuple.
public func *=( point: inout CGPoint, tuple: (CGFloat, CGFloat)) {
    point.x *= tuple.0
    point.y *= tuple.1
}
/// Returns a point by dividing the coordinates by a tuple.
public func /(point: CGPoint, tuple: (CGFloat, CGFloat)) -> CGPoint {
    return CGPoint(x: point.x / tuple.0, y: point.y / tuple.1)
}
/// Modifies the x and y values by dividing the coordinates by a tuple.
public func /=( point: inout CGPoint, tuple: (CGFloat, CGFloat)) {
    point.x /= tuple.0
    point.y /= tuple.1
}
/// Returns a point by dividing the coordinates by a factor.
public func /(point: CGPoint, factor: CGFloat) -> CGPoint {
    return CGPoint(x: point.x / factor, y: point.y / factor)
}
/// Modifies the x and y values by dividing the coordinates by a factor.
public func /=( point: inout CGPoint, factor: CGFloat) {
    point.x /= factor
    point.y /= factor
}

/// Returns a point by adding another size.
public func +(s1: CGSize, s2: CGSize) -> CGSize {
    return CGSize(width: s1.width + s2.width, height: s1.height + s2.height)
}
/// Modifies the width and height values by adding another size.
public func +=( s1: inout CGSize, s2: CGSize) {
    s1.width += s2.width
    s1.height += s2.height
}
/// Returns a point by subtracting another size.
public func -(s1: CGSize, s2: CGSize) -> CGSize {
    return CGSize(width: s1.width - s2.width, height: s1.height - s2.height)
}
/// Modifies the width and height values by subtracting another size.
public func -=( s1: inout CGSize, s2: CGSize) {
    s1.width -= s2.width
    s1.height -= s2.height
}

/// Returns a point by adding a tuple.
public func +(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize {
    return CGSize(width: size.width + tuple.0, height: size.height + tuple.1)
}
/// Modifies the width and height values by adding a tuple.
public func +=( size: inout CGSize, tuple: (CGFloat, CGFloat)) {
    size.width += tuple.0
    size.height += tuple.1
}
/// Returns a point by subtracting a tuple.
public func -(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize {
    return CGSize(width: size.width - tuple.0, height: size.height - tuple.1)
}
/// Modifies the width and height values by subtracting a tuple.
public func -=( size: inout CGSize, tuple: (CGFloat, CGFloat)) {
    size.width -= tuple.0
    size.height -= tuple.1
}
/// Returns a point by multiplying the size with a factor.
public func *(size: CGSize, factor: CGFloat) -> CGSize {
    return CGSize(width: size.width * factor, height: size.height * factor)
}
/// Modifies the width and height values by multiplying them with a factor.
public func *=( size: inout CGSize, factor: CGFloat) {
    size.width *= factor
    size.height *= factor
}
/// Returns a point by multiplying the size with a tuple.
public func *(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize {
    return CGSize(width: size.width * tuple.0, height: size.height * tuple.1)
}
/// Modifies the width and height values by multiplying them with a tuple.
public func *=( size: inout CGSize, tuple: (CGFloat, CGFloat)) {
    size.width *= tuple.0
    size.height *= tuple.1
}
/// Returns a point by dividing the size by a factor.
public func /(size: CGSize, factor: CGFloat) -> CGSize {
    return CGSize(width: size.width / factor, height: size.height / factor)
}
/// Modifies the width and height values by dividing them by a factor.
public func /=( size: inout CGSize, factor: CGFloat) {
    size.width /= factor
    size.height /= factor
}
/// Returns a point by dividing the size by a tuple.
public func /(size: CGSize, tuple: (CGFloat, CGFloat)) -> CGSize {
    return CGSize(width: size.width / tuple.0, height: size.height / tuple.1)
}
/// Modifies the width and height values by dividing them by a tuple.
public func /=( size: inout CGSize, tuple: (CGFloat, CGFloat)) {
    size.width /= tuple.0
    size.height /= tuple.1
}

/// Returns a rect by adding the coordinates of a point to the origin.
public func +(rect: CGRect, point: CGPoint) -> CGRect {
    return CGRect(origin: rect.origin + point, size: rect.size)
}
/// Modifies the x and y values by adding the coordinates of a point.
public func +=( rect: inout CGRect, point: CGPoint) {
    rect.origin += point
}
/// Returns a rect by subtracting the coordinates of a point from the origin.
public func -(rect: CGRect, point: CGPoint) -> CGRect {
    return CGRect(origin: rect.origin - point, size: rect.size)
}
/// Modifies the x and y values by subtracting the coordinates from a point.
public func -=( rect: inout CGRect, point: CGPoint) {
    rect.origin -= point
}

/// Returns a rect by adding a size to the size.
public func +(rect: CGRect, size: CGSize) -> CGRect {
    return CGRect(origin: rect.origin, size: rect.size + size)
}
/// Modifies the width and height values by adding a size.
public func +=( rect: inout CGRect, size: CGSize) {
    rect.size += size
}
/// Returns a rect by subtracting a size from the size.
public func -(rect: CGRect, size: CGSize) -> CGRect {
    return CGRect(origin: rect.origin, size: rect.size - size)
}
/// Modifies the width and height values by subtracting a size.
public func -=( rect: inout CGRect, size: CGSize) {
    rect.size -= size
}

/// Returns a point by applying a transform.
public func *(point: CGPoint, transform: CGAffineTransform) -> CGPoint {
    return point.applying(transform)
}
/// Modifies all values by applying a transform.
public func *=( point: inout CGPoint, transform: CGAffineTransform) {
    point = point.applying(transform)
}
/// Returns a size by applying a transform.
public func *(size: CGSize, transform: CGAffineTransform) -> CGSize {
    return size.applying(transform)
}
/// Modifies all values by applying a transform.
public func *=( size: inout CGSize, transform: CGAffineTransform) {
    size = size.applying(transform)
}
/// Returns a rect by applying a transform.
public func *(rect: CGRect, transform: CGAffineTransform) -> CGRect {
    return rect.applying(transform)
}
/// Modifies all values by applying a transform.
public func *=( rect: inout CGRect, transform: CGAffineTransform) {
    rect = rect.applying(transform)
}

/// Returns a transform by concatenating two transforms.
public func *(t1: CGAffineTransform, t2: CGAffineTransform) -> CGAffineTransform {
    return t1.concatenating(t2)
}
/// Modifies all values by concatenating another transform.
public func *=( t1: inout CGAffineTransform, t2: CGAffineTransform) {
    t1 = t1.concatenating(t2)
}
